package Viewer;

import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URISyntaxException;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

import DataModel.Feature;
import DataModel.Tupel;
import Utils.DataBaseLink;

/**
 * The information window is the information tool box of GeneScapes<br>
 * Enbales creating or changing features and provides functions like<br>
 * ORF-finding, adding tags etc..
 * 
 * @author Stefan Lorenz<br>
 * <br>
 *         29.11.2012
 */
@SuppressWarnings("serial")
public class InformationWindow extends JFrame implements MouseListener {

	private Viewer viewer;
	private JEditorPane editorPane;
	private JScrollPane scrollPane;
	private String fileName;
	private Feature selectedFeature;
	private int indexExon;
	private DataBaseLink baseLink;

	/**
	 * {@link Constructor} of the information window
	 * 
	 * @param viewer
	 *            to browse in
	 */
	public InformationWindow(Viewer viewer) {
		this.setIconImage(new ImageIcon(Menue.class.getResource("../resources/analyse32.png")).getImage());
		this.setLayout(new BorderLayout());
		this.viewer = viewer;
		this.baseLink = new DataBaseLink();
		this.editorPane = new JEditorPane();
		this.scrollPane = new JScrollPane(this.editorPane);
		this.scrollPane.add(this.add(createNewAnnotation()));

		this.editorPane.setContentType("text/html");
		this.editorPane.setEditable(false);
		createChangeActions();

		this.editorPane.addMouseListener(this);
		this.editorPane.addHyperlinkListener(new HyperlinkListener() {

			public void hyperlinkUpdate(HyperlinkEvent arg0) {
				if (arg0.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
					try {
						setVisible(false);
						Desktop.getDesktop().browse(arg0.getURL().toURI());
					}
					catch (IOException e) {
						e.printStackTrace();
					}
					catch (URISyntaxException e) {
						e.printStackTrace();
					}
			}
		});
		this.add(this.scrollPane, BorderLayout.CENTER);

		JPanel south = new JPanel(new GridLayout(2, 2));
		south.setBorder(BorderFactory.createTitledBorder("Information Window"));
		south.add(new JLabel("Create/Remove Feature:"));
		south.add(new JLabel("Edit Selected Feature:"));
		south.add(createNewAnnotation());
		south.add(editSelectedAnnotation());

		this.add(south, BorderLayout.NORTH);
		this.setPreferredSize(new Dimension(400, 250));
		this.pack();
	}

	/**
	 * Adds widgets and listeners to information window
	 */
	private void createChangeActions() {
		final JButton button = new JButton("Change Content");
		this.add(button, BorderLayout.SOUTH);

		button.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent arg0) {
				viewer.setEnabledViewer(false);

				final JFrame frame = new JFrame("Feature Editor");
				frame.setIconImage(new ImageIcon(InformationWindow.class.getResource("../resources/annotator32.png")).getImage());
				frame.setLayout(new BorderLayout());
				frame.setPreferredSize(new Dimension(400, 400));
				frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
				JPanel panel = new JPanel(new GridLayout(2, 1));

				final JEditorPane positions = new JEditorPane();
				JScrollPane posPane = new JScrollPane(positions);

				final JEditorPane text = new JEditorPane();
				JScrollPane pane = new JScrollPane(text);

				panel.add(posPane);
				panel.add(pane);
				frame.add(panel, BorderLayout.CENTER);
				String t = "";
				String pos = "";
				text.setEditable(true);
				frame.setVisible(true);

				for (int i = 0; i < selectedFeature.getAdditionalInformation().size(); i++) {
					t += selectedFeature.getAdditionalInformation().get(i) + "\n";
				}
				text.setText(t);

				for (int j = 0; j < selectedFeature.getListOfPositions().size(); j++) {
					Tupel tupel = selectedFeature.getListOfPositions().get(j);
					pos += tupel.getStart() + "," + tupel.getStop() + "\n";
				}
				positions.setText(pos);

				JButton save = new JButton("Apply");
				save.addActionListener(new ActionListener() {

					public void actionPerformed(ActionEvent arg0) {
						String[] split = text.getText().split("\n");
						ArrayList<String> newAddi = new ArrayList<String>();
						for (int i = 0; i < split.length; i++) {
							if (split[i].contains("/gene="))
								selectedFeature.setName(split[i].substring(7, split[i].length() - 1));
							if (split[i].contains("/locus_tag="))
								selectedFeature.setName(split[i].substring(12, split[i].length() - 1));
							if (split[i].startsWith("/"))
								newAddi.add(split[i]);
							else if (selectedFeature.getType().equals("gbk") || selectedFeature.getType().equals("embl")) {
								JOptionPane.showMessageDialog(viewer, "Tag does not start with '/'", "Warning", JOptionPane.ERROR_MESSAGE);
								return;
							}
						}
						selectedFeature.setAdditionalInformation(newAddi);
						try {
							String[] splitPos = positions.getText().split("\n");

							ArrayList<Tupel> newPos = new ArrayList<Tupel>();
							for (int i = 0; i < splitPos.length; i++) {
								newPos.add(new Tupel(Integer.valueOf(splitPos[i].split(",")[0]), Integer.valueOf(splitPos[i].split(",")[1])));
								if (newPos.get(i).getStart() > newPos.get(i).getStop())
									throw new Exception();
							}
							selectedFeature.setListOfPositions(newPos);
							selectedFeature.setAbsoultePositions(new Tupel(selectedFeature.getListOfPositions().get(0).getStart(), selectedFeature.getListOfPositions().get(selectedFeature.getListOfPositions().size() - 1).getStop()));

						}
						catch (Exception e) {
							JOptionPane.showMessageDialog(viewer, "Wrong format for positions", "Warning", JOptionPane.ERROR_MESSAGE);
							return;
						}
						frame.dispose();
						viewer.setEnabledViewer(true);
					}
				});
				frame.add(save, BorderLayout.SOUTH);
				frame.setLocation(getLocation());
				frame.pack();
				setVisible(false);
			}
		});
	}

	/**
	 * Contains all actions to create/remove a feature
	 * 
	 * @return the combobox for annotation
	 */
	private JComboBox<String> createNewAnnotation() {
		final JComboBox<String> combo = new JComboBox<String>();
		combo.setLightWeightPopupEnabled(true);
		combo.addItem("Select");
		combo.addItem("----------------------");
		combo.addItem("Take Template Feature");
		combo.addItem("Create New Novel Feature");
		combo.addItem("Take Start & Stop");
		combo.addItem("Take Start");
		combo.addItem("Take Stop");
		combo.addItem("Remove Feature");
		combo.addItem("Remove Bucket");
		combo.addItem("----------------------");
		combo.addItem("Add gene & CDS Feature");
		combo.addItem("Add Other Feature");
		combo.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				switch (combo.getSelectedIndex()) {
				case 2:
					viewer.createNewBucketWithEmptyFeature();
					viewer.getCurrentCreatedFeature().setName(selectedFeature.getName());
					viewer.getCurrentCreatedFeature().setStrand(selectedFeature.getStrand());
					viewer.getCurrentCreatedFeature().setType(selectedFeature.getType());
					viewer.getCurrentCreatedFeature().setLocus(selectedFeature.getLocus());
					viewer.getCurrentCreatedFeature().setTrackIndex(selectedFeature.getTrackIndex());
					ArrayList<String> addiList = new ArrayList<String>();
					for (int i = 0; i < selectedFeature.getAdditionalInformation().size(); i++) {
						if (selectedFeature.getAdditionalInformation().get(i).startsWith("/"))
							addiList.add(selectedFeature.getAdditionalInformation().get(i));
						else
							addiList.add("/note=\"" + selectedFeature.getAdditionalInformation().get(i) + "\"");
					}
					viewer.getCurrentCreatedFeature().setAdditionalInformation(addiList);
					viewer.setFeatureUnpositioned(-2);
					break;
				case 3:
					viewer.addNewNovelFeature(selectedFeature.getStrand(), selectedFeature.getTrackIndex(), -2);
					break;
				case 4:
					try {
						viewer.setPositionToActualCreatedTupel(selectedFeature.getListOfPositions().get(indexExon).getStart());
						viewer.setPositionToActualCreatedTupel(selectedFeature.getListOfPositions().get(indexExon).getStop());
					}
					catch (NullPointerException no) {
						JOptionPane.showMessageDialog(viewer, "Create feature first", "INFORMATION!", JOptionPane.INFORMATION_MESSAGE);
					}
					break;
				case 5:
					try {
						viewer.setPositionToActualCreatedTupel(selectedFeature.getListOfPositions().get(indexExon).getStart());
					}
					catch (NullPointerException no) {
						JOptionPane.showMessageDialog(viewer, "Create feature first", "INFORMATION!", JOptionPane.INFORMATION_MESSAGE);
					}
					break;
				case 6:
					try {
						viewer.setPositionToActualCreatedTupel(selectedFeature.getListOfPositions().get(indexExon).getStop());
					}
					catch (NullPointerException no) {
						JOptionPane.showMessageDialog(viewer, "Create feature first", "INFORMATION!", JOptionPane.INFORMATION_MESSAGE);
					}
					break;
				case 7:
					viewer.removeAnnotatedFeature(selectedFeature);
					viewer.getDrawPanel().setInfoWindowVisible(false);
					break;
				case 8:
					viewer.removeBucket(selectedFeature);
					viewer.getDrawPanel().setInfoWindowVisible(false);
					break;
				case 10:
					try {
						viewer.addGeneAndCDS(viewer.getNewAnnotationList().get(viewer.getBucketIndexOfFeature(selectedFeature)));
					}
					catch (java.lang.ArrayIndexOutOfBoundsException e2) {
						JOptionPane.showMessageDialog(viewer, "Is feature bucket created?", "INFORMATION!", JOptionPane.INFORMATION_MESSAGE);
					}
					break;
				case 11:
					viewer.addEmptyFeatureToBucketOf(selectedFeature);
					viewer.setFeatureUnpositioned(-2);
					break;

				default:
					break;
				}
				viewer.getDrawPanel().repaint();
				combo.setSelectedIndex(0);
			}
		});
		return combo;
	}

	/**
	 * Contains all actions to change the selected feature
	 * 
	 * @return the combobox for editing
	 */
	private JComboBox<String> editSelectedAnnotation() {
		final JComboBox<String> combo = new JComboBox<String>();
		combo.setLightWeightPopupEnabled(true);
		combo.addItem("Select");
		combo.addItem("----------------------");
		combo.addItem("+");
		combo.addItem("-");
		combo.addItem("gene");
		combo.addItem("mRNA");
		combo.addItem("CDS");
		combo.addItem("ncRNA");
		combo.addItem("Other Type");
		combo.addItem("----------------------");
		combo.addItem("3' antisense");
		combo.addItem("5' antisense");
		combo.addItem("full antisense");
		combo.addItem("fused");
		combo.addItem("predicted orientation");
		combo.addItem("dubious");
		combo.addItem("Set Tag...");
		combo.addItem("Set Score...");
		combo.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (selectedFeature == null) {
					setVisible(false);
					JOptionPane.showMessageDialog(null, "No Feature Is Selected!", "Information", JOptionPane.WARNING_MESSAGE);
				}
				else {
					switch (combo.getSelectedIndex()) {
					case 2:
						selectedFeature.setStrand("+");
						break;
					case 3:
						selectedFeature.setStrand("-");
						break;
					case 4:
						selectedFeature.setType("gene");
						break;
					case 5:
						selectedFeature.setType("mRNA");
						break;
					case 6:
						selectedFeature.setType("CDS");
						break;
					case 7:
						selectedFeature.setType("ncRNA");
						break;
					case 8:
						selectedFeature.setType("Other Type");
						setVisible(false);
						String inputValue = JOptionPane.showInputDialog("Please Enter Type");
						while (inputValue.contains(" ")) {
							JOptionPane.showMessageDialog(null, "Type Not Allowed! Remove Whitespace!", "Information", JOptionPane.WARNING_MESSAGE);
							inputValue = JOptionPane.showInputDialog("Please Enter Type");
						}
						if (inputValue.equals("")) {
							break;
						}
						if (inputValue != null)
							selectedFeature.setType(inputValue);
						break;
					case 10:
						selectedFeature.getAdditionalInformation().add("/note=\"3'_antisense\"");
						break;
					case 11:
						selectedFeature.getAdditionalInformation().add("/note=\"5'_antisense\"");
						break;
					case 12:
						selectedFeature.getAdditionalInformation().add("/note=\"full_antisense\"");
						break;
					case 13:
						selectedFeature.getAdditionalInformation().add("/note=\"fused\"");
						break;
					case 14:
						selectedFeature.getAdditionalInformation().add("/note=\"predicted orientation\"");
						break;
					case 15:
						selectedFeature.getAdditionalInformation().add("/note=\"dubious\"");
						break;
					case 16:
						setVisible(false);
						String addTag = JOptionPane.showInputDialog("Please Enter Note");
						if (addTag == null)
							break;
						if (addTag.equals(""))
							break;
						while (!addTag.startsWith("/")) {
							JOptionPane.showMessageDialog(null, "Tag Must Start With '/'", "Information", JOptionPane.WARNING_MESSAGE);
							addTag = JOptionPane.showInputDialog("Please Enter Note");
						}
						selectedFeature.getAdditionalInformation().add(addTag);
						break;
					case 17:
						setVisible(false);
						String addScore = JOptionPane.showInputDialog("Please Enter Note");
						if (addScore == null)
							break;
						if (addScore.equals(""))
							break;
						float score = 0;
						try {
							addScore = addScore.replace(",", ".");
							score = Float.parseFloat(addScore);
							selectedFeature.setScore(score);
						}
						catch (Exception e2) {
							JOptionPane.showMessageDialog(null, "Not a numeric entry", "Information", JOptionPane.WARNING_MESSAGE);
						}
						break;
					default:
						break;
					}
					viewer.getDrawPanel().repaint();
					combo.setSelectedIndex(0);
					setTextFromFeature();
				}
			}
		});
		return combo;
	}

	/**
	 * Sets text to the editor
	 * 
	 * @param text
	 *            text to set
	 */
	public void setText(String text) {
		this.editorPane.setText(text);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent arg0) {
		if (arg0.getButton() == 3)
			this.setVisible(false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent arg0) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent arg0) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent arg0) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent arg0) {
	}

	/**
	 * Sets the selected feature to show in information window
	 * 
	 * @param describedFeatures
	 *            selected feature to show
	 * @param indexExon
	 *            index of the hovered exon of the feature
	 * @param fileName
	 *            filename where data is from
	 */
	public void setDescribedFeatures(Feature describedFeatures, int indexExon, String fileName) {
		this.selectedFeature = describedFeatures;
		this.fileName = fileName;
		this.indexExon = indexExon;
		setTextFromFeature();
	}

	/**
	 * Returns the selected feature which is shown in the information window
	 * 
	 * @return the selected feature
	 */
	public Feature getDescribedFeatures() {
		return selectedFeature;
	}

	/**
	 * Sets the text of the selected feature to the editor of the information
	 * window and creates links to databases
	 */
	private void setTextFromFeature() {
		String toolTipText = "<html><body>";
		toolTipText += "File:" + fileName + "<br>";
		toolTipText += "<b>Name:</b> " + this.selectedFeature.getName() + "<br>";
		toolTipText += "<b>Reference:</b> " + this.selectedFeature.getLocus() + "<br>";
		toolTipText += "<b>Type:</b> " + this.selectedFeature.getType() + "<br>";
		toolTipText += "<b>Positions:</b> ";
		for (int i = 0; i < this.selectedFeature.getListOfPositions().size(); i++) {
			toolTipText += this.selectedFeature.getListOfPositions().get(i).getStart() + " " + this.selectedFeature.getListOfPositions().get(i).getStop() + " ; ";
		}
		toolTipText += "<br><b>Score:</b> " + this.selectedFeature.getScore();

		for (int i = 0; i < this.selectedFeature.getAdditionalInformation().size(); i++) {

			if (this.selectedFeature.getAdditionalInformation().get(i).contains("/db_xref=")) {
				String tmp = this.selectedFeature.getAdditionalInformation().get(i);
				if (this.baseLink.get(tmp.substring(0, tmp.lastIndexOf(":"))) != null) {
					toolTipText += "<br><a href=" + this.baseLink.get(tmp.substring(0, tmp.lastIndexOf(":"))) + tmp.substring(tmp.indexOf(":") + 1, tmp.length() - 1) + ">" + tmp + "</a>";
				}
				else
					toolTipText += "<br>" + tmp;
			}
			else if (!this.selectedFeature.getAdditionalInformation().get(i).contains("/translation=")) {
				toolTipText += "<br>" + this.selectedFeature.getAdditionalInformation().get(i);
			}
		}
		toolTipText += "</body></html>";
		this.editorPane.setText(toolTipText);
		this.editorPane.setCaretPosition(0);
	}
}
